#include <core.p4>
#include <v1model.p4>

const bit<16> TYPE_IPV4 = 0x800;
const bit<16> TYPE_IPV6 = 0x86DD;
const bit<16> TYPE_SRCROUTING = 0x1234;
#define MAX_HOPS 9

typedef bit<9>  egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
typedef bit<128> ip6Addr_t;

header ethernet_t{
    macAddr_t dstAddr;
    macAddr_t srcAddr;
    bit<16> etherType;
}
header srcRoute_t {
    bit<1>    bos;
    bit<15>   port;
}
header ipv4_t {
    bit<4>    version;
    bit<4>    ihl;
    bit<8>    diffserv;
    bit<16>   totalLen;
    bit<16>   identification;
    bit<3>    flags;
    bit<13>   fragOffset;
    bit<8>    ttl;
    bit<8>    protocol;
    bit<16>   hdrChecksum;
    ip4Addr_t srcAddr;
    ip4Addr_t dstAddr;
}
header ipv6_t{
    bit<4> version;
    bit<8> trafficClass;
    bit<20> flowLabel;
    bit<16> payLoadLen;
    bit<8> nextHdr;
    bit<8> hopLimit;
    bit<128> srcAddr;
    bit<128> dstAddr;
}

struct metadata{
    bit<16> original_etherType;
}
struct headers{
    ethernet_t ethernet;
    srcRoute_t[MAX_HOPS] srcRoutes;
    ipv4_t ipv4;
    ipv6_t ipv6;
}

parser MyParser(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
    state start{ transition parse_ethernet; }
    state parse_ethernet{
        packet.extract(hdr.ethernet);
        meta.original_etherType = hdr.ethernet.etherType;
        transition select(hdr.ethernet.etherType){
            TYPE_SRCROUTING: parse_srcRouting;
            TYPE_IPV4: parse_ipv4;
            TYPE_IPV6: parse_ipv6;
            default: accept;
        }
    }
    state parse_srcRouting {
        packet.extract(hdr.srcRoutes.next);
        transition select(hdr.srcRoutes.last.bos) {
            1: parse_after_srcRouting;
            default: parse_srcRouting;
        }
    }
    state parse_after_srcRouting {
        transition select(hdr.ethernet.etherType) {
            TYPE_IPV4: parse_ipv4;
            TYPE_IPV6: parse_ipv6;
            default: accept;
        }
    }
    state parse_ipv4 { packet.extract(hdr.ipv4); transition accept; }
    state parse_ipv6 { packet.extract(hdr.ipv6); transition accept; }
}

control MyVerifyChecksum(inout headers hdr, inout metadata meta){ apply{} }

control MyIngress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata){
    action drop() { mark_to_drop(standard_metadata); }

    action srcRoute_nhop() {
        standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port;
        hdr.srcRoutes.pop_front(1);
    }
    action srcRoute_finish() {
        hdr.ethernet.etherType = meta.original_etherType;
    }
    action update_ttl(){
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }
    action update_hopLimit() {
        hdr.ipv6.hopLimit = hdr.ipv6.hopLimit - 1;
    }
    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl-1;
    }
    action ipv6_forward(macAddr_t dstAddr, egressSpec_t port){
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv6.hopLimit = hdr.ipv6.hopLimit - 1;
    }

    // 固定多模态静态分发（multicast group 10）
    action multimode_trigger() {
        standard_metadata.mcast_grp = 10;
    }

    // 多模态专用表，每个副本用egress_spec区分
    action set_mode_ipv4_h2() {
        hdr.ethernet.etherType = TYPE_IPV4;
        hdr.ipv4.setValid();
        hdr.ipv6.setInvalid();
        hdr.srcRoutes[0].setInvalid();
        hdr.ethernet.dstAddr = 0x080000000444;
        hdr.ipv4.dstAddr = 0x0a000404;
    }
    action set_mode_ipv6_h2() {
        hdr.ethernet.etherType = TYPE_IPV6;
        hdr.ipv6.setValid();
        hdr.ipv4.setInvalid();
        hdr.srcRoutes[0].setInvalid();
        hdr.ethernet.dstAddr = 0x080000000444;
        hdr.ipv6.dstAddr = 0xfe800000000000000000000000005678;
    }
    action set_mode_sr_ipv4_h2() {
        hdr.ethernet.etherType = TYPE_SRCROUTING;
        hdr.srcRoutes[0].setValid();
        hdr.ipv4.setValid();
        hdr.ipv6.setInvalid();
        hdr.ethernet.dstAddr = 0x080000000444;
        hdr.ipv4.dstAddr = 0x0a000404;
    }
    action set_mode_sr_ipv6_h2() {
        hdr.ethernet.etherType = TYPE_SRCROUTING;
        hdr.srcRoutes[0].setValid();
        hdr.ipv4.setInvalid();
        hdr.ipv6.setValid();
        hdr.ethernet.dstAddr = 0x080000000444;
        hdr.ipv6.dstAddr = 0xfe800000000000000000000000005678;
    }

    // 多模态modality表：egress_spec决定副本模态
    table multimode_modality {
        key = { standard_metadata.egress_spec : exact; }
        actions = {
            set_mode_ipv4_h2;
            set_mode_ipv6_h2;
            set_mode_sr_ipv4_h2;
            set_mode_sr_ipv6_h2;
            NoAction;
        }
        size = 8;
        default_action = NoAction();
    }

    table multimode_dispatch {
        key = { hdr.ipv4.dstAddr: exact; }
        actions = { multimode_trigger; NoAction; }
        size = 2;
        default_action = NoAction();
    }
    table ipv4_lpm {
        key = { hdr.ipv4.dstAddr: lpm; }
        actions = { ipv4_forward; drop; NoAction; }
        size = 1024;
        default_action = NoAction();
    }
    table ipv6_lpm{
        key = { hdr.ipv6.dstAddr: lpm; }
        actions = { ipv6_forward; drop; NoAction; }
        size = 1024;
        default_action = drop();
    }

    apply {
        // 多模态调度
        if (hdr.ipv4.isValid() && hdr.ipv4.dstAddr == 0x0a000004) {
            multimode_dispatch.apply();
            multimode_modality.apply();
            return;
        }
        // 源路由包
        if (hdr.srcRoutes[0].isValid()){
            if (hdr.srcRoutes[0].bos == 1){
                srcRoute_finish();
            }
            srcRoute_nhop();
            if (hdr.ipv4.isValid()){ update_ttl(); }
            else if (hdr.ipv6.isValid()) { update_hopLimit(); }
        }
        else if (hdr.ipv4.isValid()){ ipv4_lpm.apply(); }
        else if(hdr.ipv6.isValid()){ ipv6_lpm.apply(); }
    }
}

control MyEgress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata){ apply{} }

control MyComputeChecksum(inout headers hdr,inout metadata meta){
    apply {
        update_checksum(
            hdr.ipv4.isValid(),
            { hdr.ipv4.version,
                hdr.ipv4.ihl,
                hdr.ipv4.diffserv,
                hdr.ipv4.totalLen,
                hdr.ipv4.identification,
                hdr.ipv4.flags,
                hdr.ipv4.fragOffset,
                hdr.ipv4.ttl,
                hdr.ipv4.protocol,
                hdr.ipv4.srcAddr,
                hdr.ipv4.dstAddr },
            hdr.ipv4.hdrChecksum,
            HashAlgorithm.csum16);
    }
}

control MyDeparser(packet_out packet,in headers hdr){
    apply{
        packet.emit(hdr.ethernet);
        packet.emit(hdr.srcRoutes);
        packet.emit(hdr.ipv4);
        packet.emit(hdr.ipv6);
    }
}

V1Switch(
    MyParser(),
    MyVerifyChecksum(),
    MyIngress(),
    MyEgress(),
    MyComputeChecksum(),
    MyDeparser()
)main;
